/*
 * CalendarSeeker.java  0.3 2014/02/24
 *
 * This code is created for self educational purpose, so anyone can use it. Author rights reserved.
 */


package edu.practice1;

import java.util.*;
import java.lang.String;
import edu.practice1.*;

/**
 * Java SE Basics Practice 1
 * Task 4. Calendar Seeker
 * The CalendarSeeker.taskResult() is called from Practice1Results class.
 * 
 * Business-Logic Requirements:
 * Implement a program that prints out the day of the week for a given day (1..31), month (1..12) and year. For example, for a given input "10 October 2010" it should print to the screen "Sunday"
 * 
 * Use runTest() method for tests.
 *
 * @version 	0.3 23 Feb 2014
 * @author 	Yuri Pyvovarenko
 *
 * General requirements to applications are:
 * 		•    Application must have accessible User Interface;
 * 		•    Application must have foolproof (check of entering data);
 * 		•    Application must be developed with minimum number of Java base classes – all the rest must be implemented autonomously. For example, Calendar Seeker can be easily implemented with the help of Date class but is very inappropriate in this module.
 * 		•    Application with its interim releases must be placed in SVN repository. 
 * 		•    When writing a code try to make it self-explanatory, but not confusing.
*/
public class CalendarSeeker {

	static final boolean IS_TEST = false;
	
	/** taskResult is called from Practice1Results class */
	public static void taskResult(boolean isPackageFinalized) {
		System.out.println("\n--- CalendarSeeker ---");

		if ((isPackageFinalized) && (!IS_TEST)) {
			MyDate date = inputDate();
			if (null != date) printDayOfWeek(date);
		} else {
			runTests();
		}
	}
	
	/** isLeapYear() static method is public to be used not only in this class. */
	public static boolean isLeapYear(int year) {
		boolean isLeap = false;
		if ((year % 100 == 0) && (year % 400 != 0)) isLeap = false;
		else if (0 == year % 4)	isLeap = true;
		return isLeap;
	}
	
	
	/**
	 * Class MyDate is used mainly to store date value parts in a set of String and int variables.
	 * It has no user interface output.
	 * Zero values of int variables mean an error in data received by constructor or in internal calculations.
	 */
	static class MyDate {
		String day = "0";
		String month = "0";
		String year = "0";
		int intYear = 0;
		int intDay = 0;
		int intMonth = 0;
		int maxDaysInMonth = 28; 					 // must be updated by getIntMonth() BEFORE getIntDay() call!
		
		/**
		 * Constructor takes String parameters and assignes int values of the year, month and day.
		 * If an int value equals zero it means that value was not defined properly (error occured).
		*/
		public MyDate(String aday, String amonth, String ayear) {
			day = aday;
			month = amonth;
			year = ayear;
			intYear = getIntYear(); 			//must be calculated FIRST!
			intMonth = getIntMonth(); 		//must be calculated SECOND. It will set maxDaysInMonth according to the month. Number of days in February depends on leap year.
			intDay = getIntDay(); 				//must be calculated THIRD! For instance the number of days in February (it's number should be calculates before) depends on leap year.
		}
		
		/** getIntYear() must be called FIRST when getting integer values of year, month and day! */
		int getIntYear() { 
			if (PrimeNumbers.isNotNegativeIntegerNumber(year)) {
				return (Integer.valueOf(year)).intValue();
			} else {
				return 0;
			}
		}
		
		/** getIntDay() must be called THIRD when getting integer values of year, month and day! */
		int getIntDay() { 
			int monthDayNumber = (Integer.valueOf(day)).intValue();
			if ((PrimeNumbers.isNotNegativeIntegerNumber(day))
						&& (monthDayNumber <= maxDaysInMonth)) {
				return monthDayNumber;
			} else {
				return 0;
			}
		}
		
		/** getIntMonth() must be called SECOND when getting integer values of year, month and day! */
		int getIntMonth() {
			//we'll also check if the day number is valid
			int result = 0;
			if (month.equalsIgnoreCase("january")) {
				result = 1;
				maxDaysInMonth = 31; 
			} else if (month.equalsIgnoreCase("february")) {
				result = 2; 
				if (isLeapYear(intYear)) {
					maxDaysInMonth = 29;
				} else {
					maxDaysInMonth = 28; 
				}
			} else if (month.equalsIgnoreCase("march")) {
				result = 3; 
				maxDaysInMonth = 31; 
			} else if (month.equalsIgnoreCase("april")) {
				result = 4; 
				maxDaysInMonth = 30; 
			} else if (month.equalsIgnoreCase("may")) {
				result = 5; 
				maxDaysInMonth = 31; 
			} else if (month.equalsIgnoreCase("june")) {
				result = 6; 
				maxDaysInMonth = 30; 
			} else if (month.equalsIgnoreCase("july")) {
				result = 7; 
				maxDaysInMonth = 31; 
			} else if (month.equalsIgnoreCase("august")) {
				result = 8;  
				maxDaysInMonth = 31; 
			} else if (month.equalsIgnoreCase("september")) {
				result = 9; 
				maxDaysInMonth = 30; 
			} else if (month.equalsIgnoreCase("october")) {
				result = 10;  
				maxDaysInMonth = 31; 
			} else if (month.equalsIgnoreCase("november")) {
				result = 11; 
				maxDaysInMonth = 30; 
			} else if (month.equalsIgnoreCase("december")) {
				result = 12; 
				maxDaysInMonth = 31; 
			}
			return result;
		}
		
		/** toString() method is overrided here*/
		public String toString() {
			return ("day = " + day + ", month = "  + month + ", year = "  + year);
		}
	}

	
	/** printDayOfWeek() calculates and outputs the day of week based on MyDate instance received. */
	static void printDayOfWeek(MyDate date) {
		if (null != date) {
			System.out.println("We'll try to find out DayOfWeek for "  + date.toString());
			if ( (date.intDay > 0) && (date.intMonth > 0) && (date.intYear > 0)) {
				int a = (14 - date.intMonth) / 12;
				int y = date.intYear - a;
				int m = date.intMonth + 12 * a - 2;
				int weekDay = (7000 + (date.intDay + y + y/4 - y/100 + y/400 + (31*m)/12)) % 7;
				
				DaysOfWeek dayOfWeek = DaysOfWeek.SU;
				boolean isOk = true;
				/* practice of emun usage */
				switch (weekDay) {
					case 0:
						dayOfWeek = DaysOfWeek.SU;
						break;
					case 1: 
						dayOfWeek = DaysOfWeek.MO; 
						break;
					case 2: 
						dayOfWeek = DaysOfWeek.TU; 
						break;
					case 3: 
						dayOfWeek = DaysOfWeek.WE; 
						break;
					case 4: 
						dayOfWeek = DaysOfWeek.TH; 
						break;
					case 5: 
						dayOfWeek = DaysOfWeek.FR; 
						break;
					case 6: 
						dayOfWeek = DaysOfWeek.SA; 
						break;
					default: 
						System.out.println("Error in calculations of weekDay"); 
						isOk = false; 
						break;
				}
				
				if (isOk) System.out.println("It is " + dayOfWeek.getName());
			} else System.out.println("Entered data is wrong!");
		} else System.out.println("Wrong data! Goodbye!");
	}
	
	/**
	 * You can modify DaysOfWeek enum to get name of the day of week in another language.
	 */
	enum DaysOfWeek {
			SU("Sunday"),
			MO("Monday"),
			TU("Tuesday"),
			WE("Wednesday"),
			TH("Thursday"),
			FR("Friday"),
			SA("Saturday");

		private String name;
		
		/* enum constructor */
		DaysOfWeek(String name) {
			this.name = name;
		}
			
		public String getName() {
			return name;
		}
	}
	
	/* inputDate() is to get date from user or exit the app. */
	static MyDate inputDate() {
		Scanner in = new Scanner(System.in);
		String inputedValue = new String();
		MyDate result = null;

		do {
			System.out.println("Please enter a date in format 10 October 2013 (or EXIT to exit): ");
			inputedValue = (in.nextLine()).trim();
			if (inputedValue.equalsIgnoreCase("EXIT")) break;
			result = dateStringToMyDate(inputedValue);
		} while (null == result);
		
		in.close();
		return result;
	}

	/* Casting String to MyDate. */
	static MyDate dateStringToMyDate(String inputedValue) {
		String day = "";
		String month = "";
		String year = "";
		MyDate result = null;
		
		/* get and preliminary test parts of date */
		//inputedValue = inputedValue.trim();
		int startIndex = 0;
		int endIndex = inputedValue.indexOf(" ");
		if (endIndex > 0) {
			day = inputedValue.substring(0,endIndex);
			startIndex = endIndex+1;
			endIndex = inputedValue.indexOf(" ", startIndex);
			if (endIndex > startIndex) {
				month = inputedValue.substring(startIndex,endIndex);
				startIndex = endIndex+1;
				if (month.length() > 2) {
					year = inputedValue.substring(startIndex,inputedValue.length());
					if ((year.length() > 0) && (year.length() < 10)){
						//System.out.println("Thank you. The date looks like valid.");
						result = new MyDate(day, month, year);
					}
					else System.out.println("The year is zero or too long for processing!");
				} else System.out.println("The month can not be recognized!");
			} else System.out.println("No second delimiter found. The month can not be recognized!");
		} else System.out.println("No delimiters found. The date can not be recognized!");
		
		return result;
	}
		
	static void runTests() {
		/* dateString in tests should be trimmed, OR need to uncomment trim() call in method above */
		String dateString = "";
		MyDate date = null;

		dateString="29 january 2014";
		System.out.println("\nTest string: " + dateString);
		date = dateStringToMyDate(dateString);
		printDayOfWeek(date);
		
		dateString="10 October 20100999999990";
		System.out.println("\nTest string: " + dateString);
		date = dateStringToMyDate(dateString);
		printDayOfWeek(date);
		
		dateString="29 FEBRuary 2014";
		System.out.println("\nTest string: " + dateString);
		date = dateStringToMyDate(dateString);
		printDayOfWeek(date);
		
		dateString="90 FEBRuary 1111";
		System.out.println("\nTest string: " + dateString);
		date = dateStringToMyDate(dateString);
		printDayOfWeek(date);
		
		dateString="9 FEBRuary 0000";
		System.out.println("\nTest string: " + dateString);
		date = dateStringToMyDate(dateString);
		printDayOfWeek(date);
		
		dateString="  10 janUARY 10000  ";
		System.out.println("\nTest string: " + dateString);
		date = dateStringToMyDate(dateString);
		printDayOfWeek(date);

		dateString="00 december 9999";
		System.out.println("\nTest string: " + dateString);
		date = dateStringToMyDate(dateString);
		printDayOfWeek(date);
		
		dateString="10 Octobbb 2010";
		System.out.println("\nTest string: " + dateString);
		date = dateStringToMyDate(dateString);
		printDayOfWeek(date);

		int year = 1300;
		System.out.println("\nTest if year " + year + " is leap: " + isLeapYear(year));

		year = 2012;
		System.out.println("\nTest if year " + year + " is leap: " + isLeapYear(year));
		
		year = 2014;
		System.out.println("\nTest if year " + year + " is leap: " + isLeapYear(year));
		/*				
		dateString="17 FEBRuary 2014";
		System.out.println("\nTest string: " + dateString);
		date = dateStringToMyDate(dateString);
		printDayOfWeek(date);
		
		dateString="19 FEBRuary 2014";
		System.out.println("\nTest string: " + dateString);
		date = dateStringToMyDate(dateString);
		printDayOfWeek(date);
		
		dateString="01 FEBRuary 2054";
		System.out.println("\nTest string: " + dateString);
		date = dateStringToMyDate(dateString);
		printDayOfWeek(date);
		
		dateString="01 FEBRuary 2014";
		System.out.println("\nTest string: " + dateString);
		date = dateStringToMyDate(dateString);
		printDayOfWeek(date);
		*/
	}
}
